/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2020-2021.
 * Description: decoupling mmap_mem_tracer into this file
 * Author: lijiahuan
 * Create: 2020-12-21
 */
#include <linux/rtos_devmem.h>
#include <linux/rtos_sysctl.h>

#if defined(CONFIG_RTOS_KBOX)
#include <linux/set_kbox_region.h>

unsigned int sysctl_dev_mem_tracer_log_size;
int mmap_kbox_region_id = -1;

#define MAX_UINT (~0U)
#define MAX_LOG_SIZE (MAX_UINT/1024)

#endif
int tracer_devmem_enabled;

static void tracer_printer(const struct file *filep,
				unsigned long long pa_start,
				unsigned long long pa_end,
				unsigned long va_start,
				unsigned long va_end)
{
	if (!filep)
		goto print_trace;
	if (!filep->f_path.dentry)
		goto print_trace;
	if (!filep->f_path.dentry->d_name.name)
		goto print_trace;

	pr_info("[/dev/%s]", filep->f_path.dentry->d_name.name);
print_trace:
	pr_info("Process %s[%d] mmap phys addr[0x%llx, 0x%llx], virtual addr[0x%pK, 0x%pK].\n",
		current->comm, current->pid, pa_start, pa_end,
		(void *)va_start, (void *)va_end);
}

void __mmap_mem_tracer(const struct file *filep,
				unsigned long long pa_start,
				unsigned long long pa_end,
				unsigned long va_start, unsigned long va_end)
{
#if defined(CONFIG_RTOS_KBOX)
	int old_region_id = -1;
#endif
	if (tracer_devmem_enabled) {
#ifdef CONFIG_RTOS_KBOX
		if (mmap_kbox_region_id >= 0)
			old_region_id = set_kbox_region(mmap_kbox_region_id);
#endif
		tracer_printer(filep, pa_start, pa_end, va_start, va_end);
#ifdef CONFIG_RTOS_KBOX
		if (old_region_id >= 0)
			restore_kbox_region(old_region_id);
#endif
	}
}
EXPORT_SYMBOL(__mmap_mem_tracer);

void mmap_mem_cetartlb_tracer(struct file *filep, unsigned long addr,
				unsigned long size,
				unsigned long pfn)
{
	__mmap_mem_tracer(filep,
		((unsigned long long)pfn << PAGE_SHIFT) - (unsigned long long)size,
		(unsigned long long)pfn << PAGE_SHIFT,
		addr - size, addr);
}

#if defined(CONFIG_RTOS_KBOX)
int proc_dev_mem_tracer_log_size(struct ctl_table *table, int write,
				void __user *buffer,
				size_t *lenp, loff_t *ppos)
{
	int tmpid, ret;
	unsigned int pre_size, region_size;

	if (!write)
		return proc_dointvec_minmax(table, write, buffer, lenp, ppos);

	pre_size = sysctl_dev_mem_tracer_log_size;

	ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
	if (ret)
		return ret;

	if (sysctl_dev_mem_tracer_log_size == 0) {
		sysctl_dev_mem_tracer_log_size = pre_size;
		return -EINVAL;
	}

	if (sysctl_dev_mem_tracer_log_size > MAX_LOG_SIZE) {
		pr_err("sysctl_dev_mem_tracer_log_size is too large!\n");
		sysctl_dev_mem_tracer_log_size = pre_size;
		return -EINVAL;
	}

	region_size = sysctl_dev_mem_tracer_log_size * 1024;
	tmpid = kern_kbox_register_region("dev_mem_tracer", region_size, NULL);
	if (tmpid < 0) {
		sysctl_dev_mem_tracer_log_size = pre_size;
		pr_err("register dev_mem_tracer kbox region failed with %d!\n",
			tmpid);
		return tmpid;
	}

	mmap_kbox_region_id = tmpid;
	return 0;
}
#endif

static struct ctl_table rtos_devmem_sysctls[] = {
#ifdef CONFIG_RTOS_KBOX
	{
		.procname       = "dev_mem_tracer_log_size",
		.data           = &sysctl_dev_mem_tracer_log_size,
		.maxlen         = sizeof(unsigned int),
		.mode           = 0640,
		.proc_handler   = proc_dev_mem_tracer_log_size,
	},
#endif
	{
		.procname       = "tracer_devmem",
		.data           = &tracer_devmem_enabled,
		.maxlen         = sizeof(int),
		.mode           = 0640,
		.proc_handler   = proc_dointvec_minmax,
		.extra1         = SYSCTL_ZERO,
		.extra2         = SYSCTL_ONE,
	},
	{}
};

rtos_kernel_sysctls_proc_init(rtos_devmem, rtos_devmem_sysctls);

